home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / dev / gui / FoxGuiSource.lha / FoxLibSource / TreeControl.c < prev    next >
C/C++ Source or Header  |  2001-07-07  |  38KB  |  1,212 lines

  1. /* FoxGUI - The fast, flexible, free Amiga GUI system
  2.     Copyright (C) 2001 Simon Fox (Foxysoft)
  3.  
  4. This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  5. Foxysoft: www.foxysoft.co.uk      Email:simon@foxysoft.co.uk                */
  6.  
  7. /******************************************************************************
  8.  * Shared library code.  Cannot call functions which use exit() such as:
  9.  * printf(), fprintf()
  10.  *
  11.  * Otherwise:
  12.  * The linker returns "__XCEXIT undefined" and the program will fail.
  13.  * This is because you must not exit() a library!
  14.  *
  15.  * Also:
  16.  * proto/exec.h must be included instead of clib/exec_protos.h and
  17.  * __USE_SYSBASE must be defined.
  18.  *
  19.  * Otherwise:
  20.  * The linker returns "Absolute reference to symbol _SysBase" and the
  21.  * library crashes.  Presumably the same is true for the other protos.
  22.  ******************************************************************************/
  23.  
  24. #define __USE_SYSBASE
  25.  
  26. #include <proto/mathieeedoubbas.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <stdarg.h>
  30. #include <string.h>
  31. #include <math.h>
  32.  
  33. #include <proto/graphics.h>
  34. #include <proto/intuition.h>
  35. #include "/FoxInclude/FoxGui.h"
  36.  
  37. #include "FoxGuiTools.h"
  38.  
  39. #define XOFFSET 10
  40. #define PLUSMINUSBUTTONWIDTH    11
  41. #define SCROLL_BUTTON_WIDTH        16
  42. #define SCROLL_BUTTON_HEIGHT    10
  43.  
  44. __far __stdargs int PlusMinusButtonFn(PushButton *pb);
  45.  
  46. static __inline __regargs void TreeControlClipOn(TreeControl *tc)
  47. {
  48.     if (tc && !(tc->WidgetData->flags & LB_CLIPPED))
  49.     {
  50.         struct Region *rg;
  51.         int llborder = tc->WidgetData->left + tc->LBorder + 2;
  52.         int left = llborder, right = tc->WidgetData->left + tc->points[0] - tc->LBorder - 2;
  53.  
  54.         rg = ClipGuiWindow(tc->Win, left, tc->WidgetData->top + tc->TBorder + 2,
  55.                 right, tc->WidgetData->top + tc->WidgetData->height - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0) - tc->TBorder - 3);
  56.         if (rg)
  57.             DisposeRegion(rg);
  58.         tc->WidgetData->flags |= LB_CLIPPED;
  59.     }
  60. }
  61.  
  62. static __inline __regargs void TreeControlClipOff(TreeControl *tc)
  63. {
  64.     if (tc && tc->WidgetData->flags & LB_CLIPPED)
  65.     {
  66.         UnclipGuiWindow(tc->Win);
  67.         tc->WidgetData->flags &= ~LB_CLIPPED;
  68.     }
  69. }
  70.  
  71. static int DrawTreeItem(TreeItem *ti, int top, BOOL recurse, BOOL undraw)
  72. {
  73.     if (ti)
  74.     {
  75.         // Draw line to parent.  Even if the item itself is outside
  76.         // the current view, the line may still pass through it.
  77.         if (ti->parent)
  78.         {
  79.             if (undraw)
  80.                 ti->LineToParent.FrontPen = ti->treecontrol->Win->Win->RPort->BgPen;
  81.             else
  82.             {
  83.                 ti->LineToParent.FrontPen = ti->treecontrol->FrontPen;
  84.                 ti->points[0] = ti->points[2] = ti->parent->it.LeftEdge + (short) floor(PLUSMINUSBUTTONWIDTH / 2.0) + 1;
  85.                 ti->points[1] = CalcItemTop(ti->parent) + ti->it.ITextFont->ta_YSize;
  86.                 ti->points[3] = ti->points[5] = top + (ti->it.ITextFont->ta_YSize / 2);
  87.                 ti->points[4] = ti->it.LeftEdge;
  88.             }
  89.             DrawBorder(ti->treecontrol->Win->Win->RPort, &ti->LineToParent, ti->treecontrol->WidgetData->left + ti->treecontrol->xOffset, ti->treecontrol->WidgetData->top);
  90.         }
  91.  
  92.         // Only draw the item if it is in the visible area of the tree
  93.         // control even though the area is clipped (it will reduce time).
  94.         if (top + ti->it.ITextFont->ta_YSize < ti->treecontrol->WidgetData->height - 4 - (2 * ti->treecontrol->TBorder)
  95.                 - (ti->treecontrol->LR ? SCROLL_BUTTON_HEIGHT : 0) && top >= 0)
  96.         {
  97.             int PenStore;
  98.             if (undraw)
  99.             {
  100.                 PenStore = ti->it.FrontPen;
  101.                 ti->it.FrontPen = ti->treecontrol->Win->Win->RPort->BgPen;
  102.                 if (ti->plusminus)
  103.                 {
  104.                     ti->plusminus->WidgetData->ParentControl = NULL;
  105.                     Destroy(ti->plusminus, TRUE);
  106.                     ti->plusminus = NULL;
  107.                 }
  108.                 if (ti == ti->treecontrol->hiitem)
  109.                     AreaColFill(ti->treecontrol->Win->Win->RPort, ti->treecontrol->WidgetData->left + 2 +
  110.                             ti->treecontrol->LBorder + (ti->bmi ? ti->bm->width : 0) +
  111.                             (ti->firstchild ? PLUSMINUSBUTTONWIDTH + 2 : 0) + ti->treecontrol->xOffset +
  112.                             ti->it.LeftEdge, top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder +
  113.                             ti->it.TopEdge, IntuiTextLength(&ti->it), ti->treecontrol->Font->ta_YSize,
  114.                             GetBackCol(ti->treecontrol->WidgetData->Parent));
  115.                 if (ti->bmi)
  116.                 {
  117.                     HideBitMap(ti->bmi);
  118.                     ti->bmi = NULL;
  119.                 }
  120.             }
  121.             else
  122.             {
  123.                 if (ti->firstchild)
  124.                 {
  125.                     if (ti->plusminus && ti->plusminus->button.LeftEdge == ti->treecontrol->WidgetData->left + 2 +
  126.                             ti->treecontrol->LBorder + ti->it.LeftEdge && ti->plusminus->button.TopEdge ==
  127.                             top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder)
  128.                         RefreshGList(&ti->plusminus->button, ti->treecontrol->Win->Win, NULL, 1L);
  129.                     else if (ti->plusminus)
  130.                     {
  131.                         ti->plusminus->WidgetData->ParentControl = NULL;
  132.                         DestroyButton(ti->plusminus, FALSE);
  133.                         ti->plusminus = NULL;
  134.                     }
  135.                     if (ti->treecontrol->xOffset + ti->it.LeftEdge >= 0 && !ti->plusminus)
  136.                     {
  137.                         ti->plusminus = MakeButton(ti->treecontrol->Win, "", ti->treecontrol->xOffset +
  138.                             ti->treecontrol->WidgetData->left + 2 + ti->treecontrol->LBorder + ti->it.LeftEdge,
  139.                             top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder,
  140.                             PLUSMINUSBUTTONWIDTH, ti->it.ITextFont->ta_YSize,
  141.                             ((ti->flags & TI_OPEN) ? '-' : '+'),
  142.                             ((ti->flags & TI_OPEN) ? &ti->treecontrol->minus : &ti->treecontrol->plus),
  143.                             PlusMinusButtonFn, BN_STD | BN_CLEAR | THREED, NULL);
  144.                         if (ti->plusminus)
  145.                             ti->plusminus->WidgetData->ParentControl = (Widget *) ti;
  146.                     }
  147.                 }
  148.                 if (ti->bm && !ti->bmi)
  149.                     ti->bmi = ShowBitMap(ti->bm, ti->treecontrol->Win, (unsigned short)
  150.                         (ti->treecontrol->xOffset + ti->treecontrol->WidgetData->left + 2 + ti->treecontrol->LBorder +
  151.                         ti->it.LeftEdge + (ti->firstchild ? PLUSMINUSBUTTONWIDTH + 2 : 0)),
  152.                         (unsigned short) (top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder),
  153.                         ti->bm->flags);
  154.                 if (ti == ti->treecontrol->hiitem)
  155.                     AreaColFill(ti->treecontrol->Win->Win->RPort, ti->treecontrol->WidgetData->left + 2 +
  156.                             ti->treecontrol->LBorder + (ti->bm ? ti->bm->width : 0) +
  157.                             (ti->firstchild ? PLUSMINUSBUTTONWIDTH + 2 : 0) + ti->treecontrol->xOffset +
  158.                             ti->it.LeftEdge, top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder +
  159.                             ti->it.TopEdge, IntuiTextLength(&ti->it), ti->treecontrol->Font->ta_YSize,
  160.                             ti->treecontrol->FrontPen);
  161.             }
  162.             PrintIText(ti->treecontrol->Win->Win->RPort, &ti->it, ti->treecontrol->WidgetData->left + 2 +
  163.                 ti->treecontrol->LBorder + (ti->bm ? ti->bm->width : 0) +
  164.                 (ti->firstchild ? PLUSMINUSBUTTONWIDTH + 2 : 0) + ti->treecontrol->xOffset,
  165.                 top + ti->treecontrol->WidgetData->top + 2 + ti->treecontrol->TBorder);
  166.             if (undraw)
  167.                 ti->it.FrontPen = PenStore;
  168.         }
  169.         if (ti->firstchild && (ti->flags & TI_OPEN) && recurse)
  170.             top = DrawTreeItem(ti->firstchild, top + ti->it.ITextFont->ta_YSize, recurse, undraw);
  171.         if (ti->next && recurse)
  172.             top = DrawTreeItem(ti->next, top + ti->it.ITextFont->ta_YSize, recurse, undraw);
  173.     }
  174.     return top;
  175. }
  176.  
  177. /* Returns TRUE if an item is visibly open (i.e. it's parent
  178. and it's parents parent etc must also be open) */
  179. BOOL FOXLIB ItemIsOpen(REGA0 TreeItem *it)
  180. {
  181.     if (!(it->flags & TI_OPEN))
  182.         return FALSE;
  183.     else if (it->parent)
  184.         return ItemIsOpen(it->parent);
  185.     else
  186.         return TRUE;
  187. }
  188.  
  189. int TCScrollUpButtFn(PushButton *pb);
  190. int TCScrollDownButtFn(PushButton *pb);
  191. int TCScrollLeftButtFn(PushButton *pb);
  192. int TCScrollRightButtFn(PushButton *pb);
  193.  
  194. static void CheckCreateScrollers(TreeControl *tc)
  195. {
  196.     int top = 0, maxtop = 0, maxlen = 0;
  197.     BOOL created = FALSE;
  198.     FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  199.  
  200.     if (maxtop + tc->Font->ta_YSize > tc->WidgetData->height - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0) - 4 -
  201.             (2 * tc->TBorder) && !tc->UD)
  202.     {
  203.         TreeControlClipOn(tc);
  204.         DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, TRUE); // Undraw
  205.         TreeControlClipOff(tc);
  206.         MakeVerticalScroller(tc, TCScrollUpButtFn, TCScrollDownButtFn);
  207.         created = TRUE;
  208.         if (tc->UD)
  209.         {
  210.             tc->WidgetData->flags |= SYS_LB_VSCROLL;
  211.             ResizeListBox(tc, tc->WidgetData->left, tc->WidgetData->top, tc->WidgetData->width, tc->WidgetData->height, (double) 1.0, (double) 1.0, TRUE);
  212.             if (tc->hidden == 0)
  213.             {
  214.                 AddGadget(tc->Win->Win, &tc->UD->ScrollGad, ~0);
  215.                 RefreshGList(&tc->UD->ScrollGad, tc->Win->Win, NULL, 1);
  216.             }
  217.             if (!tc->Enabled)
  218.                 DisableScroller(tc->UD);
  219.         }
  220.     }
  221.     if (maxlen > tc->WidgetData->width - (tc->UD ? SCROLL_BUTTON_WIDTH : 0) - 4 - (2 * tc->LBorder) && !tc->LR)
  222.     {
  223.         tc->WidgetData->flags |= SYS_LB_HSCROLL;
  224.         if (!created)
  225.         {
  226.             TreeControlClipOn(tc);
  227.             DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, TRUE); // Undraw
  228.             TreeControlClipOff(tc);
  229.         }
  230.         MakeHorizontalScroller(tc, TCScrollLeftButtFn, TCScrollRightButtFn);
  231.         created = TRUE;
  232.         ResizeListBox(tc, tc->WidgetData->left, tc->WidgetData->top, tc->WidgetData->width, tc->WidgetData->height, (double) 1.0, (double) 1.0, TRUE);
  233.         if (maxtop + tc->Font->ta_YSize > tc->WidgetData->height - SCROLL_BUTTON_HEIGHT - 4 -
  234.                 (2 * tc->TBorder) && !tc->UD)
  235.         {
  236.             MakeVerticalScroller(tc, TCScrollUpButtFn, TCScrollDownButtFn);
  237.             if (tc->UD)
  238.             {
  239.                 tc->WidgetData->flags |= SYS_LB_VSCROLL;
  240.                 ResizeListBox(tc, tc->WidgetData->left, tc->WidgetData->top, tc->WidgetData->width, tc->WidgetData->height, (double) 1.0, (double) 1.0, TRUE);
  241.                 if (tc->hidden == 0)
  242.                 {
  243.                     AddGadget(tc->Win->Win, &tc->UD->ScrollGad, ~0);
  244.                     RefreshGList(&tc->UD->ScrollGad, tc->Win->Win, NULL, 1);
  245.                 }
  246.                 if (!tc->Enabled)
  247.                     DisableScroller(tc->UD);
  248.             }
  249.         }
  250.     }
  251.     if (created)
  252.     {
  253.         ListBoxRefresh(tc);
  254.         TreeControlClipOn(tc);
  255.         DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, FALSE);
  256.         TreeControlClipOff(tc);
  257.     }
  258.     if (tc->UD)
  259.     {
  260.         unsigned short body, pot;
  261.  
  262.         FindScrollerValues((maxtop + tc->Font->ta_YSize) / tc->Font->ta_YSize, (tc->WidgetData->height - 4 - (2 * tc->TBorder) - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0)) / tc->Font->ta_YSize, (0 - CalcItemTop(tc->itemlist)) / tc->Font->ta_YSize, 1, &body, &pot);
  263.         NewModifyProp(&tc->UD->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEVERT | PROPNEWLOOK, 0, pot, 0, body, 1);
  264.     }
  265.     if (tc->LR)
  266.     {
  267.         unsigned short body, pot;
  268.  
  269.         FindScrollerValues(maxlen, tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0), 0 - tc->xOffset, tc->Font->ta_YSize, &body, &pot);
  270.         NewModifyProp(&tc->LR->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEHORIZ | PROPNEWLOOK, pot, 0, body, 0, 1);
  271.     }
  272. }
  273.  
  274. static void CheckDestroyScrollers(TreeControl *tc)
  275. {
  276.     int top = 0, maxtop = 0, maxlen = 0;
  277.     BOOL destroyed = FALSE;
  278.     FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  279.  
  280.     if (maxlen < tc->WidgetData->width - (tc->UD ? SCROLL_BUTTON_WIDTH : 0) && tc->LR)
  281.     {
  282.         TreeControlClipOn(tc);
  283.         DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, TRUE);
  284.         TreeControlClipOff(tc);
  285.         DestroyHorizontalScroller(tc, TRUE);
  286.         destroyed = TRUE;
  287.     }
  288.     if (maxtop + tc->Font->ta_YSize < tc->WidgetData->height - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0) && tc->UD)
  289.     {
  290.         if (!destroyed)
  291.         {
  292.             TreeControlClipOn(tc);
  293.             DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, TRUE);
  294.             TreeControlClipOff(tc);
  295.         }
  296.         DestroyVerticalScroller(tc, TRUE);
  297.         // We may have just closed a +/- button and therefore no-longer need a scroller BUT that
  298.         // doesn't necessarily mean that topshown is currently itemlist.  It should be now.
  299.         tc->topshown = tc->itemlist;
  300.  
  301.         if (maxlen < tc->WidgetData->width && tc->LR)
  302.             DestroyHorizontalScroller(tc, TRUE);
  303.         destroyed = TRUE;
  304.     }
  305.     if (destroyed)
  306.     {
  307.         TreeControlClipOn(tc);
  308.         DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, FALSE);
  309.         TreeControlClipOff(tc);
  310.     }
  311. }
  312.  
  313. static TreeItem *FindTreeItemRecurse(TreeItem *root, char *text)
  314. {
  315.     TreeItem *found = NULL;
  316.  
  317.     if (strcmp(root->it.IText, text) == 0)
  318.         return root;
  319.     if (root->firstchild)
  320.         found = FindTreeItemRecurse(root->firstchild, text);
  321.     if (root->next && !found)
  322.         found = FindTreeItemRecurse(root->next, text);
  323.     return found;
  324. }
  325.  
  326. TreeItem* FOXLIB FindTreeItem(REGA0 TreeControl *tc, REGA1 char *text)
  327. {
  328.     return FindTreeItemRecurse(tc->itemlist, text);
  329. }
  330.  
  331. void FOXLIB RemoveItem(REGA0 TreeItem *ti)
  332. {
  333.     TreeControl *tc = ti->treecontrol;
  334.     TreeItem *pp = ti, *redrawfrom = NULL, *previous = NULL, *oldtopshown = tc->topshown;
  335.  
  336.     if (ti->parent)
  337.     {
  338.         BOOL parentopen = ItemIsOpen(ti->parent);
  339.         if (parentopen)
  340.         {
  341.             while (pp->parent)
  342.                 pp = pp->parent;
  343.             TreeControlClipOn(tc);
  344.             DrawTreeItem(pp, CalcItemTop(pp), TRUE, TRUE);
  345.         }
  346.  
  347.         if (ti->parent->firstchild == ti)
  348.             ti->parent->firstchild = ti->next;
  349.         else
  350.         {
  351.             TreeItem *t = ti->parent->firstchild, *p = NULL;
  352.  
  353.             while (t != ti)
  354.             {
  355.                 p = t;
  356.                 t = t->next;
  357.             }
  358.             previous = p;
  359.         }
  360.  
  361.         if (ti == tc->hiitem)
  362.             tc->hiitem = NULL;
  363.  
  364.         if (parentopen)
  365.             redrawfrom = pp;
  366.     }
  367.     else
  368.     {
  369.         TreeControlClipOn(tc);
  370.         DrawTreeItem(ti, CalcItemTop(ti), TRUE, TRUE);
  371.  
  372.         if (ti != tc->itemlist)
  373.         {
  374.             TreeItem *t = tc->itemlist, *p = NULL;
  375.  
  376.             while (t != ti)
  377.             {
  378.                 p = t;
  379.                 t = t->next;
  380.             }
  381.             previous = p;
  382.         }
  383.  
  384.         if (ti == tc->hiitem)
  385.             tc->hiitem = NULL;
  386.  
  387.         redrawfrom = ti->next;
  388.     }
  389.  
  390.     // Now free the item and any children
  391.     if (ti->firstchild)
  392.         FreeItemTree(ti->firstchild, ti, TRUE);
  393.     if (previous)
  394.         previous->next = ti->next;
  395.     if (ti == tc->itemlist)
  396.         tc->itemlist = ti->next;
  397.     if (ti->it.IText)
  398.         GuiFree(ti->it.IText);
  399.     if (ti->bm && (ti->flags & TI_BITMAPISSCALED))
  400.         FreeGuiBitMap(ti->bm);
  401.     if (tc->topshown == ti || tc->topshown == NULL) // FreeItemTree may have set this to NULL
  402.         if (ti->next)
  403.             tc->topshown = ti->next;
  404.         else if (tc->itemlist)
  405.             tc->topshown = FindPreviousItem(ti);
  406.         else
  407.             tc->topshown = NULL;
  408.  
  409.     CheckDestroyScrollers(tc);
  410.  
  411.     GuiFree(ti);
  412.  
  413.     if (redrawfrom)
  414.     {
  415.         if (tc->topshown != oldtopshown)
  416.             redrawfrom = tc->topshown;
  417.         DrawTreeItem(redrawfrom, CalcItemTop(redrawfrom), TRUE, FALSE);
  418.     }
  419.     TreeControlClipOff(tc);
  420. }
  421.  
  422. TreeItem* FOXLIB ReplaceTCItem(REGA0 TreeItem *old, REGA1 char *text, REGA2 GuiBitMap *bm, REGA3 void *ItemData)
  423. {
  424.     if (old)
  425.     {
  426.         char *oldIText = old->it.IText;
  427.         int top = CalcItemTop(old);
  428.  
  429.         TreeControlClipOn(old->treecontrol);
  430.         if ((!(old->parent)) || ItemIsOpen(old->parent))
  431.             DrawTreeItem(old, top, FALSE, TRUE);
  432.  
  433.         if (old->bmi)
  434.             GuiFree(old->bmi);
  435.         // Don't free the bitmap unless we allocated a copy - otherwise the user allocated it and should free
  436.         //    it.  In this way, the user can use the same bitmap for many items in the list.
  437.         if (old->bm && (old->flags & TI_BITMAPISSCALED))
  438.             FreeGuiBitMap(old->bm);
  439.         if (old->flags & TI_BITMAPISSCALED)
  440.             old->flags &= ~TI_BITMAPISSCALED;
  441.  
  442.         old->bm = bm;
  443.         if (old->bm && old->bm->height > old->treecontrol->Font->ta_YSize)
  444.         {
  445.             /* The compiler should be clever enough to know that we're using Commodore IEEE DP Maths and insert all of the necessary
  446.                 conversions at compile time.  However, it can't manage the conversion from IEEE DP to unsigned short on it's own without
  447.                 generating a reference to __XCEXIT which we just can't allow in a shared library so I've helped it along a bit by inserting
  448.                 some of the conversions.  See Includes & Autodocs for a description of the IEEE DP conversions in the mathieeedoubbas
  449.                 library */
  450.             old->bm = ScaleBitMap(old->bm, (unsigned short) IEEEDPFix((IEEEDPFlt(old->treecontrol->Font->ta_YSize) / old->bm->height) * IEEEDPFlt(old->bm->width)), (unsigned short) old->treecontrol->Font->ta_YSize);
  451.             if (old->bm)
  452.                 old->flags |= TI_BITMAPISSCALED;
  453.         }
  454.         old->bmi = NULL;
  455.  
  456.         old->it.IText = (char *) GuiMalloc((strlen(text) + 1) * sizeof(char), 0);
  457.         if (old->it.IText)
  458.             strcpy(old->it.IText, text);
  459.         if ((!(old->parent)) || ItemIsOpen(old->parent))
  460.             DrawTreeItem(old, top, FALSE, FALSE);
  461.         TreeControlClipOff(old->treecontrol);
  462.         CheckCreateScrollers(old->treecontrol);
  463.         CheckDestroyScrollers(old->treecontrol);
  464.         if (oldIText)
  465.             GuiFree(oldIText);
  466.     }
  467.     return old;
  468. }
  469.  
  470. TreeItem* FOXLIB AddItem(REGA0 TreeControl *tc, REGA1 TreeItem *InsBefore, REGA2 TreeItem *Parent, REGA3 char *text,
  471.         REGD0 BOOL IsOpen, REGD1 GuiBitMap *bm, REGD2 void *ItemData)
  472. {
  473.     TreeItem *ti = (TreeItem *) GuiMalloc(sizeof(TreeItem), 0), *pp = Parent;
  474.  
  475.     if (ti)
  476.     {
  477.         BOOL draw = TRUE;
  478.  
  479.         ti->treecontrol = tc;
  480.         ti->itemdata = ItemData;
  481.         ti->flags = (IsOpen ? TI_OPEN : 0);
  482.         ti->parent = Parent;
  483.         ti->firstchild = NULL;
  484.         ti->plusminus = NULL;
  485.         ti->it.TopEdge = 0;
  486.         ti->it.NextText = NULL;
  487.         ti->it.ITextFont = tc->Font;
  488.         ti->it.FrontPen = tc->FrontPen;
  489.         ti->it.BackPen = GetBackCol(tc->WidgetData->Parent);
  490.         ti->it.DrawMode = JAM1;
  491.         ti->bm = bm;
  492.         if (ti->bm && ti->bm->height > tc->Font->ta_YSize)
  493.         {
  494.             /* The compiler should be clever enough to know that we're using Commodore IEEE DP Maths and insert all of the necessary
  495.                 conversions at compile time.  However, it can't manage the conversion from IEEE DP to unsigned short on it's own without
  496.                 generating a reference to __XCEXIT which we just can't allow in a shared library so I've helped it along a bit by inserting
  497.                 some of the conversions.  See Includes & Autodocs for a description of the IEEE DP conversions in the mathieeedoubbas
  498.                 library */
  499.             ti->bm = ScaleBitMap(ti->bm, (unsigned short) IEEEDPFix((IEEEDPFlt(tc->Font->ta_YSize) / ti->bm->height) * IEEEDPFlt(ti->bm->width)), tc->Font->ta_YSize);
  500.             if (ti->bm)
  501.                 ti->flags |= TI_BITMAPISSCALED;
  502.         }
  503.         ti->bmi = NULL;
  504.         ti->LineToParent.LeftEdge = /*tc->WidgetData->left +*/ 2 + tc->LBorder;
  505.         ti->LineToParent.TopEdge = /*tc->WidgetData->top +*/ 2 + tc->TBorder;
  506.         ti->LineToParent.DrawMode = JAM1;
  507.         ti->LineToParent.XY = ti->points;
  508.         ti->LineToParent.Count = 3;
  509.         ti->LineToParent.NextBorder = NULL;
  510.  
  511.         ti->it.IText = (char *) GuiMalloc((strlen(text) + 1) * sizeof(char), 0);
  512.         if (ti->it.IText)
  513.             strcpy(ti->it.IText, text);
  514.  
  515.         if (Parent)
  516.         {
  517.             // There's no need to redraw anything if the parent is closed UNLESS the parent is visible
  518.             // and this is the first child in which case the parent will need to be redrawn as the +/-
  519.             // button is created.
  520.             if (!ItemIsOpen(Parent) && !(Parent->firstchild == NULL &&
  521.                     (Parent->parent == NULL || ItemIsOpen(Parent->parent))))
  522.                 draw = FALSE;
  523.  
  524.             if (draw)
  525.             {
  526.                 while (pp->parent)
  527.                     pp = pp->parent;
  528.  
  529.                 TreeControlClipOn(tc);
  530.                 DrawTreeItem(pp, CalcItemTop(pp), TRUE, TRUE);
  531.             }
  532.  
  533.             ti->it.LeftEdge = Parent->it.LeftEdge + XOFFSET;
  534.             if (!Parent->firstchild)
  535.             {
  536.                 Parent->firstchild = ti;
  537.                 ti->next = NULL;
  538.             }
  539.             else // Parent already has a child.
  540.             {
  541.                 TreeItem *i = Parent->firstchild, *p = NULL;
  542.  
  543.                 while (i && i != InsBefore)
  544.                 {
  545.                     p = i;
  546.                     i = i->next;
  547.                 }
  548.                 if (i == Parent->firstchild)
  549.                     Parent->firstchild = ti;
  550.                 else
  551.                     p->next = ti;
  552.                 ti->next = i;
  553.             }
  554.  
  555.             if (draw)
  556.             {
  557.                 DrawTreeItem(pp, CalcItemTop(pp), TRUE, FALSE);
  558.                 TreeControlClipOff(tc);
  559.             }
  560.         }
  561.         else // No Parent
  562.         {
  563.             TreeItem *i = tc->itemlist, *p = NULL;
  564.             ti->it.LeftEdge = 0;
  565.  
  566.             if (i) // At least one item already in list
  567.             {
  568.                 while (i && i != InsBefore)
  569.                 {
  570.                     p = i;
  571.                     i = i->next;
  572.                 }
  573.  
  574.                 TreeControlClipOn(tc);
  575.                 DrawTreeItem(i, CalcItemTop(i), TRUE, TRUE);
  576.  
  577.                 ti->next = p->next;
  578.                 p->next = ti;
  579.             }
  580.             else // No items in list yet
  581.             {
  582.                 tc->itemlist = tc->topshown = ti;
  583.                 ti->next = NULL;
  584.  
  585.                 TreeControlClipOn(tc);
  586.             }
  587.             DrawTreeItem(ti, CalcItemTop(ti), TRUE, FALSE);
  588.             TreeControlClipOff(tc);
  589.         }
  590.         CheckCreateScrollers(tc);
  591.     }
  592.     return ti;
  593. }
  594.  
  595. __far __stdargs int PlusMinusButtonFn(PushButton *pb)
  596. {
  597.     TreeItem *ti = (TreeItem *) pb->WidgetData->ParentControl, *pp = ti;
  598.     TreeControl *tc = ti->treecontrol;
  599.     int top, retval = GUI_CONTINUE;
  600.  
  601.     if ((tc->WidgetData->flags & TC_OPENITEM) && !(ti->flags & TI_OPEN))
  602.         retval = ((TCIntFnPtr) *(tc->Eventfn))(tc, TC_OPENITEM, ti, NULL);
  603.     else if ((tc->WidgetData->flags & TC_CLOSEITEM) && (ti->flags & TI_OPEN))
  604.         retval = ((TCIntFnPtr) *(tc->Eventfn))(tc, TC_CLOSEITEM, ti, NULL);
  605.  
  606.     if (retval != GUI_END)
  607.     {
  608.         while (pp->parent)
  609.             pp = pp->parent;
  610.  
  611.         top = CalcItemTop(pp);
  612.  
  613.         TreeControlClipOn(tc);
  614.         DrawTreeItem(pp, top, TRUE, TRUE); // Will destroy the button.
  615.         if (ti->flags & TI_OPEN)
  616.             ti->flags &= ~TI_OPEN;
  617.         else
  618.             ti->flags |= TI_OPEN;
  619.  
  620.         DrawTreeItem(pp, top, TRUE, FALSE); // Will create the new button.
  621.         TreeControlClipOff(tc);
  622.  
  623.         if (ti->flags & TI_OPEN)
  624.             // We've just opened up a node - check whether we now need any scroll bars.
  625.             CheckCreateScrollers(tc);
  626.  
  627.         if ((tc->UD || tc->LR) && !(ti->flags & TI_OPEN))
  628.             // We've just closed a node - check whether we still need any scroll bars.
  629.             CheckDestroyScrollers(tc);
  630.     }
  631.     return retval;
  632. }
  633.  
  634. void FOXLIB OpenItem(REGA0 TreeItem *it)
  635. {
  636.     if (it && it->firstchild && !(it->flags & TI_OPEN))
  637.         PlusMinusButtonFn(it->plusminus);
  638. }
  639.  
  640. void FOXLIB CloseItem(REGA0 TreeItem *it)
  641. {
  642.     if (it && it->firstchild && (it->flags & TI_OPEN))
  643.         PlusMinusButtonFn(it->plusminus);
  644. }
  645.  
  646. static TreeItem *LV(int *top, TreeItem *ti)
  647. {
  648.     TreeItem *n = ti;
  649.  
  650.     // Need to multiply the font height by two because we are currently at the top of the
  651.     // previous item and we want to check the position of the bottom of the next item.
  652.     if (ti->firstchild && (ti->flags & TI_OPEN) && *top + (2 * ti->it.ITextFont->ta_YSize) < ti->treecontrol->WidgetData->height - 4 - (2 * ti->treecontrol->TBorder) - (ti->treecontrol->LR ? SCROLL_BUTTON_HEIGHT : 0))
  653.     {
  654.         *top += ti->it.ITextFont->ta_YSize;
  655.         n = LV(top, ti->firstchild);
  656.     }
  657.     if (ti->next && *top + (2 * ti->it.ITextFont->ta_YSize) < ti->treecontrol->WidgetData->height - 4 - (2 * ti->treecontrol->TBorder) - (ti->treecontrol->LR ? SCROLL_BUTTON_HEIGHT : 0))
  658.     {
  659.         *top += ti->it.ITextFont->ta_YSize;
  660.         n = LV(top, ti->next);
  661.     }
  662.     return n;
  663. }
  664.  
  665. static TreeItem *LowestVisible(TreeControl *tc)
  666. {
  667.     TreeItem *ti = tc->itemlist;
  668.     int top = CalcItemTop(ti);
  669.     return LV(&top, ti);
  670. }
  671.  
  672. static TreeItem *LastOpen(TreeControl *tc)
  673. {
  674.     TreeItem *t = tc->itemlist;
  675.  
  676.     if (t)
  677.     {
  678.         while (t->next)
  679.             t = t->next;
  680.         if ((t->flags & TI_OPEN) && t->firstchild)
  681.         {
  682.             t = t->firstchild;
  683.             while (t->next || (t->firstchild && (t->flags & TI_OPEN)))
  684.             {
  685.                 if (t->next)
  686.                     t = t->next;
  687.                 else
  688.                     t = t->firstchild;
  689.             }
  690.         }
  691.     }
  692.     return t;
  693. }
  694.  
  695. void TreeControlScrollDown(TreeControl *tc, BOOL RefreshScroller)
  696. {
  697.     int top = 0, maxlen = 0, maxtop = 0;
  698.     unsigned short body, pot;
  699.     // Find the bottom item shown:
  700.     TreeItem *newtop = NULL, *pp = tc->topshown;
  701.     BOOL ishi = (tc->topshown == tc->hiitem);
  702.  
  703.     if (LastOpen(tc) == LowestVisible(tc))
  704.         return; // Nothing open to scroll down to.
  705.  
  706.     if (tc->topshown->firstchild && (tc->topshown->flags & TI_OPEN))
  707.         newtop = tc->topshown->firstchild;
  708.     else if (tc->topshown->next)
  709.         newtop = tc->topshown->next;
  710.     else
  711.     {
  712.         TreeItem *ti = tc->topshown;
  713.  
  714.         while (ti->parent && !newtop)
  715.         {
  716.             ti = ti->parent;
  717.             if (ti->next)
  718.                 newtop = ti->next;
  719.         }
  720.  
  721.         if (!newtop) // nothing to scroll down to
  722.             return;
  723.     }
  724.  
  725.     TreeControlClipOn(tc);
  726.     while (pp->parent)
  727.         pp = pp->parent;
  728.     DrawTreeItem(pp, CalcItemTop(pp), TRUE, TRUE);
  729.     if (ishi && (tc->WidgetData->flags & TC_REHILIGHT_ON_SCROLL))
  730.     {
  731.         tc->hiitem->it.DrawMode = JAM1;
  732.         tc->hiitem = newtop;
  733.         tc->hiitem->it.DrawMode = JAM2 | INVERSVID;
  734.     }
  735.     tc->topshown = newtop;
  736.     DrawTreeItem(pp, CalcItemTop(pp), TRUE, FALSE);
  737.     TreeControlClipOff(tc);
  738.  
  739.     if (RefreshScroller)
  740.     {
  741.         FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  742.         FindScrollerValues((maxtop + tc->Font->ta_YSize) / tc->Font->ta_YSize, (tc->WidgetData->height - 4 - (2 * tc->TBorder) - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0)) / tc->Font->ta_YSize, (0 - CalcItemTop(tc->itemlist)) / tc->Font->ta_YSize, 1, &body, &pot);
  743.         NewModifyProp(&tc->UD->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEVERT | PROPNEWLOOK, 0, pot, 0, body, 1);
  744.     }
  745. }
  746.  
  747. int TCScrollDownButtFn(PushButton *pb)
  748. {
  749.     TreeControlScrollDown((TreeControl*) pb->WidgetData->ParentControl, TRUE);
  750.     return GUI_CONTINUE;
  751. }
  752.  
  753. void TreeControlScrollUp(TreeControl *tc, BOOL RefreshScroller)
  754. {
  755.     BOOL ishi = (LowestVisible(tc) == tc->hiitem);
  756.  
  757.     if (tc->itemlist != tc->topshown) // Already at the top?
  758.     {
  759.         int top = 0, maxlen = 0, maxtop = 0;
  760.         unsigned short body, pot;
  761.         TreeItem *p = FindPreviousItem(tc->topshown), *pp = tc->topshown;
  762.  
  763.         TreeControlClipOn(tc);
  764.         while (pp->parent)
  765.             pp = pp->parent;
  766.         DrawTreeItem(pp, CalcItemTop(pp), TRUE, TRUE);
  767.         tc->topshown = p;
  768.         if (ishi && (tc->WidgetData->flags & TC_REHILIGHT_ON_SCROLL))
  769.         {
  770.             tc->hiitem->it.DrawMode = JAM1;
  771.             tc->hiitem = LowestVisible(tc);
  772.             tc->hiitem->it.DrawMode = JAM2 | INVERSVID;
  773.         }
  774.         pp = p;
  775.         while (pp->parent)
  776.             pp = pp->parent;
  777.         DrawTreeItem(pp, CalcItemTop(pp), TRUE, FALSE);
  778.         TreeControlClipOff(tc);
  779.  
  780.         if (RefreshScroller)
  781.         {
  782.             FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  783.             FindScrollerValues((maxtop + tc->Font->ta_YSize) / tc->Font->ta_YSize, (tc->WidgetData->height - 4 - (2 * tc->TBorder) - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0)) / tc->Font->ta_YSize, (0 - CalcItemTop(tc->itemlist)) / tc->Font->ta_YSize, 1, &body, &pot);
  784.             NewModifyProp(&tc->UD->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEVERT | PROPNEWLOOK, 0, pot, 0, body, 1);
  785.         }
  786.     }
  787. }
  788.  
  789. int TCScrollUpButtFn(PushButton *pb)
  790. {
  791.     TreeControlScrollUp((TreeControl*) pb->WidgetData->ParentControl, TRUE);
  792.     return GUI_CONTINUE;
  793. }
  794.  
  795. TreeItem *FindItemByTop(TreeControl *tc, int top);
  796.  
  797. int TCScrollLeftButtFn(PushButton *pb)
  798. {
  799.     TreeControl *tc = (TreeControl *) pb->WidgetData->ParentControl;
  800.     int i;
  801.  
  802.     if (tc->xOffset < 0)
  803.     {
  804.         int top = 0, maxlen = 0, maxtop = 0, pptop;
  805.         unsigned short body, pot;
  806.         TreeItem *pp = tc->topshown;
  807.  
  808.         while (pp->parent)
  809.             pp = pp->parent;
  810.         pptop = CalcItemTop(pp);
  811.  
  812.         TreeControlClipOn(tc);
  813.         DrawTreeItem(pp, pptop, TRUE, TRUE);
  814.         tc->xOffset += tc->Font->ta_YSize;
  815.         if (tc->xOffset > 0)
  816.             tc->xOffset = 0;
  817.         FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  818.         FindScrollerValues(maxlen, tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0), 0 - tc->xOffset, tc->Font->ta_YSize, &body, &pot);
  819.         NewModifyProp(&tc->LR->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEHORIZ | PROPNEWLOOK, pot, 0, body, 0, 1);
  820.         DrawTreeItem(pp, pptop, TRUE, FALSE);
  821.         TreeControlClipOff(tc);
  822.     }
  823.     for (i = 0; i < 30; i++)
  824.         FindItemByTop(tc, i);
  825.     return GUI_CONTINUE;
  826. }
  827.  
  828. int TCScrollRightButtFn(PushButton *pb)
  829. {
  830.     int top = 0, maxlen = 0, maxtop = 0, pptop;
  831.     TreeControl *tc = (TreeControl *) pb->WidgetData->ParentControl;
  832.  
  833.     FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &top);
  834.     if (tc->xOffset > tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0) - maxlen)
  835.     {
  836.         unsigned short body, pot;
  837.         TreeItem *pp = tc->topshown;
  838.  
  839.         while (pp->parent)
  840.             pp = pp->parent;
  841.         pptop = CalcItemTop(pp);
  842.  
  843.         TreeControlClipOn(tc);
  844.         DrawTreeItem(pp, pptop, TRUE, TRUE);
  845.         tc->xOffset -= tc->Font->ta_YSize;
  846.         if (tc->xOffset < tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0) - maxlen)
  847.             tc->xOffset = tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0) - maxlen;
  848.         FindScrollerValues(maxlen, tc->WidgetData->width - 4 - (2 * tc->LBorder) - (tc->UD ? SCROLL_BUTTON_WIDTH : 0), 0 - tc->xOffset, tc->Font->ta_YSize, &body, &pot);
  849.         NewModifyProp(&tc->LR->ScrollGad, tc->Win->Win, NULL, AUTOKNOB | FREEHORIZ | PROPNEWLOOK, pot, 0, body, 0, 1);
  850.         DrawTreeItem(pp, pptop, TRUE, FALSE);
  851.         TreeControlClipOff(tc);
  852.     }
  853.     return GUI_CONTINUE;
  854. }
  855.  
  856. TreeItem* FOXLIB TCHiItem(REGA0 TreeControl *tc)
  857. {
  858.     if (tc)
  859.         if (tc->hiitem)
  860.             return tc->hiitem;
  861.     return NULL;
  862. }
  863.  
  864. char* FOXLIB TCHiText(REGA0 TreeControl *tc)
  865. {
  866.     if (tc)
  867.         if (tc->hiitem)
  868.             return tc->hiitem->it.IText;
  869.     return NULL;
  870. }
  871.  
  872. char* FOXLIB TCItemText(REGA0 TreeItem *ti)
  873. {
  874.     if (ti)
  875.         return ti->it.IText;
  876.     return NULL;
  877. }
  878.  
  879. TreeControl* FOXLIB MakeTreeControl(REGA0 void *Parent, REGD0 int left, REGD1 int top, REGD2 int width, REGD3 int height,
  880.         REGD4 int lborder, REGD5 int tborder, REGD6 int flags,
  881.         REGA1 int (* __far __stdargs Eventfn) (TreeControl*, short, TreeItem*, void**), REGA2 void *extension)
  882. {
  883.     TreeControl *tc;
  884.  
  885.     Diagnostic("MakeTreeControl", ENTER, TRUE);
  886.  
  887.     tc = (TreeControl *) CreateListBox(Parent, left, top, width, height, lborder, tborder,
  888.         Gui.TextCol, &GuiFont, (LBIntFnPtr) Eventfn, flags, TreeControlObjectType);
  889.  
  890.     Diagnostic("MakeTreeControl", EXIT, (tc != NULL));
  891.     return tc;
  892. }
  893.  
  894. void FOXLIB SetTreeControlDragPointer(REGA0 TreeControl *tc, REGA1 unsigned short *DragPointer, REGD0 int width, REGD1 int height,
  895.         REGD2 int xoffset, REGD3 int yoffset)
  896. {
  897.     if (tc)
  898.     {
  899.         tc->DragPointer = DragPointer;
  900.         tc->PointerWidth = width;
  901.         tc->PointerHeight = height;
  902.         tc->PointerXOffset = xoffset;
  903.         tc->PointerYOffset = yoffset;
  904.     }
  905.  
  906. }
  907.  
  908. static TreeItem *FindItemByTopRecurse(TreeItem *ti, int top, int *curtop)
  909. {
  910.     TreeItem *t = NULL;
  911.  
  912.     if (*curtop + ti->it.ITextFont->ta_YSize >= top)
  913.         return ti;
  914.  
  915.     if (ti->firstchild && (ti->flags & TI_OPEN))
  916.     {
  917.         *curtop += ti->it.ITextFont->ta_YSize;
  918.         t = FindItemByTopRecurse(ti->firstchild, top, curtop);
  919.         if (t)
  920.             return t;
  921.     }
  922.     if (ti->next)
  923.     {
  924.         *curtop += ti->it.ITextFont->ta_YSize;
  925.         t = FindItemByTopRecurse(ti->next, top, curtop);
  926.     }
  927.     return t;
  928. }
  929.  
  930. TreeItem *FindItemByTop(TreeControl *tc, int top)
  931. {
  932.     int curtop = 0;
  933.     TreeItem *ti;
  934.  
  935.     ti = FindItemByTopRecurse(tc->itemlist, top, &curtop);
  936.     return ti;
  937. }
  938.  
  939. void UpdateTCScrollGadImagery(TreeControl *tc)
  940. {
  941.     unsigned short top = 0;
  942.     int maxlen = 0, maxtop = 0, curtop = 0;
  943.  
  944.     FindMaxSizes(tc->itemlist, &maxlen, &maxtop, &curtop);
  945.  
  946.     if (tc->UD)
  947.     {
  948.         unsigned short curtop = (0 - CalcItemTop(tc->itemlist)) / tc->Font->ta_YSize;
  949.  
  950.         top = FindScrollerTop((maxtop + tc->Font->ta_YSize) / tc->Font->ta_YSize, (tc->WidgetData->height - 4 - (2 * tc->TBorder) - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0)) / tc->Font->ta_YSize, tc->UD->ScrollGadInfo.VertPot);
  951.         if (top != curtop)
  952.         {
  953.             if (top == curtop - 1)
  954.                 TreeControlScrollUp(tc, FALSE);
  955.             else if (top == curtop + 1)
  956.                 TreeControlScrollDown(tc, FALSE);
  957.             else
  958.             {
  959.                 TreeItem *newtopitem, *pp = tc->topshown;
  960.                 int pixeltop = top * tc->Font->ta_YSize;
  961.                 BOOL HilightBottom = FALSE;
  962.  
  963.                 while (pp->parent)
  964.                     pp = pp->parent;
  965.                 TreeControlClipOn(tc);
  966.                 DrawTreeItem(pp, CalcItemTop(pp), TRUE, TRUE);
  967.                 newtopitem = FindItemByTop(tc, pixeltop);
  968.                 if (tc->hiitem && (tc->WidgetData->flags & TC_REHILIGHT_ON_SCROLL))
  969.                 {
  970.                     int newtopitemtop = CalcItemTop(newtopitem), hiitemtop = CalcItemTop(tc->hiitem);
  971.  
  972.                     if (top > curtop && hiitemtop >= curtop * tc->Font->ta_YSize && hiitemtop <=
  973.                             newtopitemtop)
  974.                     {
  975.                         tc->hiitem->it.DrawMode = JAM1;
  976.                         tc->hiitem = newtopitem;
  977.                         tc->hiitem->it.DrawMode = JAM2 | INVERSVID;
  978.                     }
  979.                     if (top < curtop && hiitemtop + ((curtop - top) * tc->Font->ta_YSize)
  980.                             >= (tc->WidgetData->height - 4 - (2 * tc->TBorder) - (tc->LR ? SCROLL_BUTTON_HEIGHT : 0)))
  981.                     {
  982.                         tc->hiitem->it.DrawMode = JAM1;
  983.                         HilightBottom = TRUE;
  984.                     }
  985.                 }
  986.                 pp = tc->topshown = newtopitem;
  987.                 if (HilightBottom)
  988.                 {
  989.                     tc->hiitem = LowestVisible(tc); // New Bottom Item
  990.                     tc->hiitem->it.DrawMode = JAM2 | INVERSVID;
  991.                 }
  992.                 while (pp->parent)
  993.                     pp = pp->parent;
  994.                 DrawTreeItem(pp, CalcItemTop(pp), TRUE, FALSE);
  995.                 TreeControlClipOff(tc);
  996.             }
  997.         }
  998.     }
  999.     if (tc->LR)
  1000.     {
  1001.         int left = 0 - FindScrollerTop(maxlen, tc->WidgetData->width - 4 - (2 * tc->LBorder), tc->LR->ScrollGadInfo.HorizPot);
  1002.         if (left != tc->xOffset) // Horizontal Scroller
  1003.         {
  1004.             TreeItem *pp = tc->topshown;
  1005.             int pptop;
  1006.  
  1007.             while (pp->parent)
  1008.                 pp = pp->parent;
  1009.             pptop = CalcItemTop(pp);
  1010.             TreeControlClipOn(tc);
  1011.             DrawTreeItem(pp, pptop, TRUE, TRUE);
  1012.             tc->xOffset = left;
  1013.             DrawTreeItem(pp, pptop, TRUE, FALSE);
  1014.             TreeControlClipOff(tc);
  1015.         }
  1016.     }
  1017. }
  1018.  
  1019. void TreeControlRehilight(TreeControl *tc, TreeItem *HiElem, BOOL unhilight, BOOL hilight)
  1020. {
  1021.     // If the element has not been specified then unhilight only.
  1022.  
  1023.     if (hilight || unhilight)
  1024.         TreeControlClipOn(tc);
  1025.  
  1026.     // Unhilight previous selection
  1027.     if (tc->hiitem)
  1028.     {
  1029.         BOOL parentisopen = ((tc->hiitem->parent && ItemIsOpen(tc->hiitem->parent)) || !tc->hiitem->parent);
  1030.         TreeItem *ti = tc->hiitem;
  1031.         int hitop = CalcItemTop(ti);
  1032.  
  1033.         if (unhilight && tc->hidden == 0 && parentisopen)
  1034.             DrawTreeItem(ti, hitop, FALSE, TRUE);
  1035.  
  1036.         tc->hiitem = NULL;
  1037.         ti->it.DrawMode = JAM1;
  1038.         if (unhilight && tc->hidden == 0 && parentisopen)
  1039.             DrawTreeItem(ti, hitop, FALSE, FALSE);
  1040.     }
  1041.  
  1042.     // Now hilight the new item...
  1043.     if (HiElem)
  1044.     {
  1045.         BOOL parentisopen = ((HiElem->parent && ItemIsOpen(HiElem->parent)) || !HiElem->parent);
  1046.         int newhitop = CalcItemTop(HiElem);
  1047.  
  1048.         if (hilight && tc->hidden == 0 && parentisopen)
  1049.             DrawTreeItem(HiElem, newhitop, FALSE, TRUE);
  1050.  
  1051.         tc->hiitem = HiElem;
  1052.         tc->hiitem->it.DrawMode = JAM2 | INVERSVID;
  1053.         if (hilight && tc->hidden == 0 && parentisopen)
  1054.             DrawTreeItem(tc->hiitem, newhitop, FALSE, FALSE);
  1055.     }
  1056.     if (hilight || unhilight)
  1057.         TreeControlClipOff(tc);
  1058. }
  1059.  
  1060. // This function is called by FoxGui to unhilight an item that the pointer was held over
  1061. // when the user was dragging something into this tree control, without hilighting another item.
  1062. void ClearTreeControlDropNum(TreeControl *tc, TreeItem *OldHiItem)
  1063. {
  1064.     TreeControlClipOn(tc);
  1065.  
  1066.     // Unhilight previous selection
  1067.     if (OldHiItem && OldHiItem != tc->hiitem)
  1068.     {
  1069.         BOOL parentisopen = ((OldHiItem->parent && ItemIsOpen(OldHiItem->parent)) || !OldHiItem->parent);
  1070.         TreeItem *ti = OldHiItem;
  1071.         int hitop = CalcItemTop(ti);
  1072.  
  1073.         if (tc->hidden == 0 && parentisopen)
  1074.             DrawTreeItem(ti, hitop, FALSE, TRUE);
  1075.  
  1076.         ti->it.DrawMode = JAM1;
  1077.         if (tc->hidden == 0 && parentisopen)
  1078.             DrawTreeItem(ti, hitop, FALSE, FALSE);
  1079.     }
  1080.     TreeControlClipOff(tc);
  1081. }
  1082.  
  1083. // This function is called by FoxGui to hilight the item that the pointer is held over
  1084. // when the user is dragging something into this treecontrol.
  1085. TreeItem *SetTreeControlDropNum(TreeControl *tc, TreeItem *HiItem, TreeItem *OldHiItem)
  1086. {
  1087.     if (HiItem == OldHiItem)
  1088.         return HiItem; // Nothing to do.
  1089.  
  1090.     TreeControlClipOn(tc);
  1091.  
  1092.     // Unhilight previous selection
  1093.     if (OldHiItem && OldHiItem != tc->hiitem)
  1094.     {
  1095.         BOOL parentisopen = ((OldHiItem->parent && ItemIsOpen(OldHiItem->parent)) || !OldHiItem->parent);
  1096.         TreeItem *ti = OldHiItem;
  1097.         int hitop = CalcItemTop(ti);
  1098.  
  1099.         if (tc->hidden == 0 && parentisopen)
  1100.             DrawTreeItem(ti, hitop, FALSE, TRUE);
  1101.  
  1102.         ti->it.DrawMode = JAM1;
  1103.         if (tc->hidden == 0 && parentisopen)
  1104.             DrawTreeItem(ti, hitop, FALSE, FALSE);
  1105.     }
  1106.  
  1107.     // Now hilight the new item...
  1108.     if (HiItem && HiItem != tc->hiitem)
  1109.     {
  1110.         BOOL parentisopen = ((HiItem->parent && ItemIsOpen(HiItem->parent)) || !HiItem->parent);
  1111.         int newhitop = CalcItemTop(HiItem);
  1112.  
  1113.         if (tc->hidden == 0 && parentisopen)
  1114.             DrawTreeItem(HiItem, newhitop, FALSE, TRUE);
  1115.  
  1116.         HiItem->it.DrawMode = JAM2 | INVERSVID;
  1117.         if (tc->hidden == 0 && parentisopen)
  1118.             DrawTreeItem(HiItem, newhitop, FALSE, FALSE);
  1119.     }
  1120.     TreeControlClipOff(tc);
  1121.     return HiItem;
  1122. }
  1123.  
  1124. void FOXLIB SetTreeControlHiItem(REGA0 TreeControl *tc, REGA1 TreeItem *HiItem, REGD0 BOOL refresh)
  1125. {
  1126.     TreeControlRehilight(tc, HiItem, refresh, refresh);
  1127. }
  1128.  
  1129. int TCSelect(TreeControl *tc, long x, long y, unsigned long seconds, unsigned long micros, Frame **FrameDownPtr)
  1130.     {
  1131.     static unsigned long last_seconds = 0;
  1132.     static unsigned long last_micros = 0;
  1133.     static TreeItem *last_selected = NULL;
  1134.  
  1135.     if (tc)
  1136.         {
  1137.         TreeItem *SelectedElem;
  1138.  
  1139.         y -= tc->WidgetData->top + tc->TBorder + 1;
  1140.  
  1141.         SelectedElem = FindItemByTop(tc, y - CalcItemTop(tc->itemlist));
  1142.         if (SelectedElem)
  1143.             {
  1144.             TreeControlRehilight(tc, SelectedElem, TRUE, TRUE);
  1145.  
  1146.             /*    Is this the second click of a double-click?  (If it is but the list-box has no
  1147.                 double-click function then treat this as a second single-click). */
  1148.             if ((tc->WidgetData->flags & TC_DBLCLICK) && tc->Eventfn &&
  1149.                         DoubleClick(last_seconds, last_micros, seconds, micros) &&
  1150.                         SelectedElem == last_selected)
  1151.                 {
  1152.                 // This is the second click of a double click.
  1153.                 int Stop;
  1154.                 last_selected = NULL;
  1155.                 *FrameDownPtr = NULL;
  1156.                 Stop = ((TCIntFnPtr) *(tc->Eventfn))(tc, TC_DBLCLICK, tc->hiitem, NULL);
  1157.                 return Stop;
  1158.                 }
  1159.             else // This is a single click.
  1160.                 {
  1161.                 last_seconds = seconds;
  1162.                 last_micros = micros;
  1163.                 last_selected = SelectedElem;
  1164.                 if ((tc->WidgetData->flags & TC_SELECT) && tc->Eventfn)
  1165.                     {
  1166.                     int Stop;
  1167.                     *FrameDownPtr = NULL;
  1168.                     Stop = ((TCIntFnPtr) *(tc->Eventfn))(tc, TC_SELECT, tc->hiitem, NULL);
  1169.                     return Stop;
  1170.                     }
  1171.                 }
  1172.             }
  1173.         }
  1174.     return GUI_CONTINUE;
  1175.     }
  1176.  
  1177. void FOXLIB ClearTreeControl(REGA0 TreeControl *tc)
  1178. {
  1179.     if (tc)
  1180.     {
  1181.         if (tc->itemlist)
  1182.             FreeItemTree(tc->itemlist, NULL, TRUE);
  1183.         tc->itemlist = tc->topshown = tc->hiitem = NULL;
  1184.  
  1185.         if (tc->LR)
  1186.             DestroyHorizontalScroller(tc, FALSE);
  1187.         if (tc->UD)
  1188.             DestroyVerticalScroller(tc, FALSE);
  1189.         ListBoxRefresh(tc);
  1190.     }
  1191. }
  1192.  
  1193. void* FOXLIB ItemData(REGA0 TreeItem *ti)
  1194. {
  1195.     return ti->itemdata;
  1196. }
  1197.  
  1198. void UndrawTreeControl(TreeControl *tc)
  1199. {
  1200.     TreeControlClipOn(tc);
  1201.     DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, TRUE);
  1202.     TreeControlClipOff(tc);
  1203. }
  1204.  
  1205. void DrawTreeControl(TreeControl *tc)
  1206. {
  1207.     ListBoxRefresh(tc);
  1208.     TreeControlClipOn(tc);
  1209.     DrawTreeItem(tc->itemlist, CalcItemTop(tc->itemlist), TRUE, FALSE);
  1210.     TreeControlClipOff(tc);
  1211. }
  1212.